什么是终端驱动程序
用户程序的例子有vi、emacs、pine、more、lynx、hangman、robots 等, 这些程序设置终端驱动程序的击键和输出处理方式.驱动程序有很多设置,但是用户 程序常用的到有:
- 立即响应击键时间
- 有限的输入集
- 输入的超时
- 屏蔽Ctrl-C
终端驱动程序的模式
规范模式
驱动程序输入的字符保存在缓冲区, 并且在接收到回车键的时才将缓冲区的字符 发送到程序. 缓冲区数据使驱动程序可以实现最基本的编辑功能.
非规范模式
当缓冲区和编辑功能被关闭时, 连接被称为处于非规范模式. 终端处理器仍旧进 行特定的字符处理, 但是不能编辑, 相应的输入(删除)被视作常规的数据输入.
如果希望用户能够编辑输入, 需要你的程序中实现编辑功能
raw 模式
当所有处理被关闭后, 驱动程序将输入直接传递给程序, 这种情况下, 驱动程序 被称为处于 raw 模式.
play_again.c
#include <stdio.h> #include <termios.h> #define QUESTION "Do you want another transaction" int get_response(char *); int main() { int response; response = get_response(QUESTION); return response; } int get_response(char *question) { printf("%s (y/n)?", question); while(1){ switch(getchar()){ case 'y': case 'Y': return 0; case 'n': case 'N': case EOF: return 1; } } }
这个程序必须在用户按回车键的时候才对数据进行处理, 因此, play_again0 会 把下面输入作为一个否定的回答
$ ./play_again0 Do you want another transaction (y/n)? sure thing!
第一个改进是关闭规范输入, 使得程序能够在用户敲键的同时得到输入的字符
paly_agin1.c
#include <stdio.h> #include <termios.h> #define QUESTION "Do you want another transaction" int get_response(char *); void tty_mode(int); void set_crmode(); int main() { int response; tty_mode(0); /* save tty mode */ set_crmode(); /* set chr-by-chr mode */ response = get_response(QUESTION); tty_mode(1); /* restore tty mode */ return response; } int get_response(char *question) { int input; printf("%s (y/n)?", question); while(1){ switch((input = getchar())){ case 'y': case 'Y': return 0; case 'n': case 'N': case EOF: return 1; default: printf("\ncannot understan %c, ", input); printf("Please type y or n\n"); } } } void set_crmode() { struct termios ttystate; tcgetattr(0, &ttystate); ttystate.c_lflag &= ~ICANON; /* no buffering */ ttystate.c_cc[VMIN] = 1; /* get 1 char at a time */ tcsetattr(0, TCSANOW, &ttystate); } void tty_mode(int how) { static struct termios original_mode; if (how == 0) tcgetattr(0, &original_mode); else tcsetattr(0, TCSANOW, &original_mode); }
play_again2.c ---- 忽略非法键
#include <stdio.h> #include <termios.h> #define QUESTION "Do you want another transaction" int get_response(char *); void tty_mode(int); void set_crmode(); void set_cr_noecho_mode(); int main() { int response; tty_mode(0); /* save tty mode */ set_cr_noecho_mode(); /* set -icanon, -echo */ response = get_response(QUESTION); tty_mode(1); /* restore tty mode */ return response; } int get_response(char *question) { int input; printf("%s (y/n)?", question); while(1){ switch((input = getchar())){ case 'y': case 'Y': return 0; case 'n': case 'N': case EOF: return 1; default: printf("\ncannot understan %c, ", input); printf("Please type y or n\n"); } } } void set_cr_noecho_mode() { struct termios ttystate; tcgetattr(0, &ttystate); ttystate.c_lflag &= ~ICANON; /* no buffering */ ttystate.c_lflag &= ~ECHO; /* no echo either */ ttystate.c_cc[VMIN] = 1; /* get 1 char at a time */ tcsetattr(0, TCSANOW, &ttystate); } void tty_mode(int how) { static struct termios original_mode; if (how == 0) tcgetattr(0, &original_mode); else tcsetattr(0, TCSANOW, &original_mode); }
非阻塞的输入: play_again3.c
可以使用 fcntl 为文件描述符开始 O_NDELAY 表示, 关闭文件描述符的 阻塞状态
非阻塞操作的内部实现非常简单. 每个文件都有一块保存未读取数据的地 方, 如果文件描述符置了 O_NDELAY 位, 并且那块空间是空的, read 调用返回0.
#include <stdio.h> #include <termios.h> #include <fcntl.h> #include <string.h> #define ASK "Do you want another transaction" #define TRIES 3 #define SLEEPTIME 2 #define BEEP putchar('\a') int get_response(char *, int); void tty_mode(int); void set_crmode(); void set_cr_noecho_mode(); void set_nodelay_mode(); char get_ok_char(); int main() { int response; tty_mode(0); /* save tty mode */ set_cr_noecho_mode(); /* set -icanon, -echo */ set_nodelay_mode(); response = get_response(ASK, TRIES); tty_mode(1); /* restore tty mode */ return response; } int get_response(char *question, int maxtries) { int input; printf("%s (y/n)?", question); while(1){ sleep(SLEEPTIME); input = tolower(get_ok_char()); if (input == 'y') return 0; if (input == 'n') return 1; if (maxtries-- == 0) return 2; BEEP; } } char get_ok_char() { int c; while ((c = getchar()) != EOF && strchr("yYnN", c) == NULL) ; return c; } void set_cr_noecho_mode() { struct termios ttystate; tcgetattr(0, &ttystate); ttystate.c_lflag &= ~ICANON; /* no buffering */ ttystate.c_lflag &= ~ECHO; /* no echo either */ ttystate.c_cc[VMIN] = 1; /* get 1 char at a time */ tcsetattr(0, TCSANOW, &ttystate); } void set_nodelay_mode() { int termflags; termflags = fcntl(0, F_GETFL); termflags |= O_NDELAY; fcntl(0, F_SETFL, termflags); } void tty_mode(int how) { static struct termios original_mode; static int original_flags; if (how == 0){ tcgetattr(0, &original_mode); original_flags = fcntl(0, F_GETFL); } else{ tcsetattr(0, TCSANOW, &original_mode); fcntl(0, F_SETFL, original_flags); } }